using System.Collections.Generic;
using System.Data.Common;
using System.Data;
using System.Data.SqlClient;
using Commons.Models.Definitions;
using Commons.Models.Settings;
using Commons.Models.Stats;
using Commons.Preferences;

namespace Commons.Daos.Stats
{
	public class UserStatsDao : SQLDao<UserStats>, IUserStatsDao
	{
        private const string CreateSql = "create table user_stats (user_id integer primary key, lifes integer, floor integer, credits integer, score integer, score integer, xp integer)";
        private const string SelectSql = "select lifes, floor, credits, score, xp from user_stats where user_id = ?";
        private const string InsertSql = "insert into user_stats (user_id, lifes, floor, credits, score, xp) OUTPUT INSERTED.ID values (?, ?, ?, ?, ?, ?)";
        private const string UpdateSql = "update user_stats set lifes = lifes + ?, floor = floor + ?, credits = credits + ?, score = score + ?, xp = xp + ? where user_id = ?";
		private const string deleteSql = "delete from user_stats where user_id = ?";

		protected override IEnumerable<string> GetUpgradeSqls(int oldVersion)
		{
			if (oldVersion == 0)
			{
				yield return CreateSql;
			}
		}

		public UserStats Get()
		{
			if (cache == null)
			{
            	SqlParameter userId = new SqlParameter("userId", GamePrefs.UserId);
				cache = DaoSupport.QueryForObject<UserStats>(new UserStatsRowMapper(), SelectSql, userId);
           }

			return cache;
		}

		public void Insert(UserStats stats)
		{
        	SqlParameter userId = new SqlParameter("userId", GamePrefs.UserId);
            SqlParameter lifes = new SqlParameter("lifes", stats.GetStat<SimpleStat>(StatUnit.Lifes).Value);
            SqlParameter floor = new SqlParameter("floor", stats.GetStat<SimpleStat>(StatUnit.Floor).Value);
            SqlParameter credits = new SqlParameter("credits", stats.GetStat<SimpleStat>(StatUnit.Credits).Value);
            SqlParameter coins = new SqlParameter("score", stats.GetStat<SimpleStat>(StatUnit.Score).Value);
			SqlParameter xp = new SqlParameter("xp", stats.GetStat<LeveledStat>(StatUnit.Xp).Value);

			DaoSupport.Insert(InsertSql, userId, lifes, floor, credits, coins, xp);
        	cache = stats;
		}

		public void Update(UserStats stats)
		{
			DaoSupport.AggregateExecution(stats,() =>
			{
                SqlParameter lifes = new SqlParameter("lifes", stats.GetStat<SimpleStat>(StatUnit.Lifes).Value);
                SqlParameter floor = new SqlParameter("floor", stats.GetStat<SimpleStat>(StatUnit.Floor).Value);
        		SqlParameter credits = new SqlParameter("credits", stats.GetStat<SimpleStat>(StatUnit.Credits).Delta);
                SqlParameter coins = new SqlParameter("score", stats.GetStat<SimpleStat>(StatUnit.Score).Delta);
				SqlParameter xp = new SqlParameter("xp", stats.GetStat<LeveledStat>(StatUnit.Xp).Delta);
				SqlParameter userId = new SqlParameter("userId", GamePrefs.UserId);

				stats.Apply();

				DaoSupport.Update(UpdateSql, lifes, floor, credits, coins, xp, userId);
     		});

			cache = stats;
		}

		public void Delete()
		{
      	    SqlParameter userId = new SqlParameter("userId", GamePrefs.UserId);
            DaoSupport.Delete(deleteSql, userId);
         	cache = null;
		}

		protected class UserStatsRowMapper : IRowMapper<UserStats>
		{
			public UserStats MapRow(DbDataReader reader)
			{
				StatSettings statSettings = SettingsModel.Instance.GetSettings<StatSettings>();

                int lifes = reader.GetInt32(0);
                int floor = reader.GetInt32(1);
                int credits = reader.GetInt32(2);
				int score = reader.GetInt32(3);
				int xp = reader.GetInt32(4);

				UserStats userStats = new UserStats();

                userStats.AddStat(new SimpleStat(statSettings.GetDefinition(StatUnit.Lifes), lifes));
                userStats.AddStat(new SimpleStat(statSettings.GetDefinition(StatUnit.Floor), floor));
				userStats.AddStat(new SimpleStat(statSettings.GetDefinition(StatUnit.Credits), credits));
                userStats.AddStat(new SimpleStat(statSettings.GetDefinition(StatUnit.Score), score));
				userStats.AddStat(new LeveledStat(statSettings.GetDefinition<LeveledStatDefinition>(StatUnit.Xp), xp));

				return userStats;
			}
		}
	}
}